"""表元数据调试输出命令模块"""

import argparse
import json
from pathlib import Path
from typing import Optional, Callable, Any, Dict, List
from src.core.command import Command
from src.base.registry import bera
from src.base.sql_file_utils import SQLFileUtils
from src.base.project_config import ProjectConfig
from src.core.common.derived import derive
from src.commands.gen_code import apply_with_project_config


class DumpTableMetaCommand(Command):
    """表元数据调试输出命令"""

    def __init__(self):
        super().__init__()
        self.name = "dump-table-meta"
        self.group = "调试工具"
        self.help = "输出表元数据用于调试模板变量"
        self.description = """
        输出表元数据用于调试模板变量。该命令会:
        1. 读取已生成的meta.auto.json文件
        2. 按照指定格式输出常用字段
        3. 支持自定义输出字段表达式
        4. 用于在模板渲染前验证数据正确性
        """

    def add_arguments(self, parser: argparse.ArgumentParser) -> None:
        """添加命令参数"""
        # 添加通用参数
        SQLFileUtils.add_arguments(parser)

        # 添加命令特有参数
        parser.add_argument('--format', choices=['flat', 'json'], default='flat',
                          help='输出格式：flat(扁平键值对) | json(完整JSON) (默认: flat)')
        parser.add_argument('--filter-include', '--include', nargs='*',
                          help='只显示键值包含指定字符串的字段，如: naming sql (支持多个字符串)')
        parser.add_argument('--filter-exclude', '--exclude', nargs='*',
                          help='排除键值包含指定字符串的字段，如: derived (支持多个字符串)')
        parser.add_argument('--filter-prefix', '--prefix', nargs='*',
                          help='只显示以指定前缀开头的字段，如: table.sql columns.id (支持多个前缀)')
        parser.add_argument('--no-derived', action='store_true',
                          help='跳过衍生配置处理，直接输出原始meta数据')

    def execute(self, args: argparse.Namespace, callback: Optional[Callable[[], None]] = None) -> None:
        """执行命令"""
        try:
            bera.logger.info("开始执行dump-table-meta命令")

            # 先初始化项目配置
            ProjectConfig.init(args.project_dir)

            # 先添加项目配置的sql-dir到搜索路径(优先级较低)
            sql_dir = str(ProjectConfig.get_sql_dir())
            bera.logger.debug(f"添加项目配置的SQL目录到搜索路径: {sql_dir}")
            SQLFileUtils.add_search_path(sql_dir)

            # 后执行callback,让它添加的搜索路径优先级更高
            if callback:
                callback()

            # 使用统一的输入模式处理
            SQLFileUtils.execute_file_processing(args, self._process_sql_file)

            bera.logger.info("dump-table-meta命令执行完成")
        except Exception as e:
            bera.logger.error(f"dump-table-meta命令执行失败: {str(e)}")
            if args.verbose:
                import traceback
                traceback.print_exc()
            raise

    def _process_sql_file(self, filepath: str, args: argparse.Namespace) -> None:
        """处理单个文件

        Args:
            filepath: SQL文件路径
            args: 命令行参数
        """
        try:
            # 从文件路径中提取表名
            table_name = Path(filepath).stem
            bera.logger.info(f"处理表: {table_name}")

            # 获取元数据文件路径
            meta_file = self._get_meta_file_path(table_name)

            if not meta_file.exists():
                bera.logger.error(f"元数据文件不存在: {meta_file}")
                bera.logger.info("请先运行 gen-meta 命令生成元数据文件")
                return

            # 读取并处理元数据
            meta_content = self._load_meta_data(meta_file, args)

            # 输出调试信息
            self._dump_meta_data(table_name, meta_content, args)

        except Exception as e:
            bera.logger.error(f"处理表失败: {table_name}")
            if args.verbose:
                import traceback
                traceback.print_exc()
            SQLFileUtils.handle_file_error(e, Path(filepath), table_name)

    def _get_meta_file_path(self, table_name: str) -> Path:
        """获取元数据文件路径"""
        return SQLFileUtils.get_output_table_dir(table_name) / 'meta.auto.json'

    def _load_meta_data(self, meta_file: Path, args: argparse.Namespace) -> Dict[str, Any]:
        """读取和处理元数据"""
        try:
            # 读取元数据文件
            meta_content = SQLFileUtils.read_json_file(meta_file)

            # 如果不跳过衍生配置，则处理衍生配置
            if not args.no_derived:
                meta_content = derive(meta_content)
                bera.logger.debug("已处理衍生配置")

                # 标记项目配置信息到元数据
                meta_content = apply_with_project_config(meta_content)
                bera.logger.debug("已标记项目配置信息")
            else:
                bera.logger.debug("跳过衍生配置处理")

            return meta_content

        except Exception as e:
            bera.logger.error(f"读取元数据文件失败: {str(e)}")
            raise

    def _dump_meta_data(self, table_name: str, meta_content: Dict[str, Any], args: argparse.Namespace) -> None:
        """输出元数据调试信息"""
        if args.format == 'json':
            self._dump_json_format(table_name, meta_content, args)
        else:
            # 默认使用flat格式
            self._dump_flat_format(table_name, meta_content, args)

    def _dump_json_format(self, table_name: str, meta_content: Dict[str, Any], args: argparse.Namespace) -> None:
        """JSON格式输出"""
        print(f"\n=== JSON Table Meta Debug Info: {table_name} ===")

        # 检查是否有任何过滤器
        has_filter = ((hasattr(args, 'filter_prefix') and args.filter_prefix) or
                     (hasattr(args, 'filter_include') and args.filter_include) or
                     (hasattr(args, 'filter_exclude') and args.filter_exclude))

        if has_filter:
            # 如果有过滤器，先转换为扁平数据再过滤，然后重构为JSON
            all_flat_data = {}
            self._flatten_dict(meta_content, '', all_flat_data)
            filtered_flat_data = self._filter_flat_data(all_flat_data, args)

            # 将扁平数据重构为嵌套JSON结构
            filtered_json = {}
            for key, value in filtered_flat_data.items():
                self._set_nested_value(filtered_json, key, value)
            print(json.dumps(filtered_json, indent=2, ensure_ascii=False))
        else:
            # 输出完整数据
            print(json.dumps(meta_content, indent=2, ensure_ascii=False))

    def _dump_flat_format(self, table_name: str, meta_content: Dict[str, Any], args: argparse.Namespace) -> None:
        """扁平格式输出"""
        print(f"\n=== Flat Table Meta Debug Info: {table_name} ===")

        # 步骤1: 读入所有meta.json数据到键值表
        all_flat_data = {}
        self._flatten_dict(meta_content, '', all_flat_data)

        # 步骤2: 根据条件过滤数据
        filtered_data = self._filter_flat_data(all_flat_data, args)

        # 步骤3: 排序并输出
        if not filtered_data:
            print("没有匹配的字段")
            return

        for key in sorted(filtered_data.keys()):
            value = filtered_data[key]
            print(f"{key}: {self._format_value(value)}")

    def _filter_flat_data(self, flat_data: Dict[str, Any], args: argparse.Namespace) -> Dict[str, Any]:
        """根据过滤器过滤扁平数据"""
        result_data = flat_data.copy()

        # 先处理前缀过滤器（优先级最高）
        if hasattr(args, 'filter_prefix') and args.filter_prefix:
            filtered_data = {}
            for key, value in flat_data.items():
                # 检查键是否以任一指定前缀开头
                for prefix in args.filter_prefix:
                    if key.startswith(prefix):
                        filtered_data[key] = value
                        break  # 匹配一个即可，跳出内层循环
            result_data = filtered_data

        # 再处理包含过滤器
        if hasattr(args, 'filter_include') and args.filter_include:
            filtered_data = {}
            for key, value in result_data.items():
                # 检查键是否包含任一指定字符串
                for include_str in args.filter_include:
                    if include_str in key:
                        filtered_data[key] = value
                        break  # 匹配一个即可，跳出内层循环
            result_data = filtered_data

        # 最后处理排除过滤器
        if hasattr(args, 'filter_exclude') and args.filter_exclude:
            keys_to_remove = []
            for key in result_data.keys():
                # 检查键是否包含任一排除字符串
                for exclude_str in args.filter_exclude:
                    if exclude_str in key:
                        keys_to_remove.append(key)
                        break  # 匹配一个即可，跳出内层循环

            # 移除匹配的键
            for key in keys_to_remove:
                result_data.pop(key, None)

        return result_data

    def _flatten_dict(self, data: Dict[str, Any], prefix: str, result: Dict[str, Any]) -> None:
        """递归展开字典为扁平格式"""
        for key, value in data.items():
            new_key = f"{prefix}.{key}" if prefix else key

            if isinstance(value, dict):
                # 如果是字典，递归展开
                self._flatten_dict(value, new_key, result)
            else:
                # 如果不是字典，直接添加到结果中
                result[new_key] = value

    def _get_nested_value(self, data: Dict[str, Any], path: str) -> Any:
        """根据路径获取嵌套字典中的值"""
        try:
            current = data
            for key in path.split('.'):
                if isinstance(current, dict) and key in current:
                    current = current[key]
                else:
                    return None
            return current
        except Exception:
            return None

    def _set_nested_value(self, data: Dict[str, Any], path: str, value: Any) -> None:
        """根据路径设置嵌套字典中的值"""
        keys = path.split('.')
        current = data
        for key in keys[:-1]:
            if key not in current:
                current[key] = {}
            current = current[key]
        current[keys[-1]] = value

    def _format_value(self, value: Any) -> str:
        """格式化值用于输出"""
        if value is None:
            return "null"
        elif isinstance(value, str):
            return f'"{value}"'
        elif isinstance(value, bool):
            return str(value).lower()
        elif isinstance(value, (list, dict)):
            return json.dumps(value, ensure_ascii=False)
        else:
            return str(value)